/**
 * Crazy Table Filter
 *  License by CrazyYi.
 *  v1.1.0
 *  v1.3.0 20200228: 独立设置setting
 *  v1.4.0 20200426: 新增是否开启批量编辑功能
 *
 *  reference: https://lolicode.gitee.io/tablefilter/
 */
const filterModules = [
    'table', 'jquery', 'form', 'layer',
    'crazyTool', 'crazyQuery', 'crazyData', 'crazyHttp', 'crazyBox', 'crazyIcon'
];

layui.define(filterModules, function (exports) {
    const MOD_NAME = 'crazyFilter',
        MOD_MODE = 'filter'
    const TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
    const FORM_CANCEL = 'filterFormCancel',
        FORM_BatEdit = 'filterFormBatEdit'

    let $ = layui.jquery,
        $tQuery = layui.crazyQuery,
        $Box = layui.crazyBox,
        $Icon = layui.crazyIcon,
        crazyTool = layui.crazyTool,
        $Data = layui.crazyData,
        $Http = layui.crazyHttp,
        layer = layui.layer;

    let gHttp = {};

    /**
     * 默认参数，语法定义，参考了layui.table 的options 的方式
     * reference: https://www.layui.com/doc/modules/table.html#options
     */
    const defaultOptions = {
        "v": '1.0.0',
        mode: 'local',  // 过滤模式，local, api
        logOn: false, // 是否开启 log 打印
        done: function (config) {},

        config: [],    //过滤项配置，一维数组
    };

    /**
     * 保存当前的 options
     */
    let gOptions = {};

    let gMainTable;
    let gRes = {};
    let gData = [];

    let gConfig = {
        type: 'input',      // input, checkbox, radio，select，datetime
        format: undefined,  // 仅当 type=datetime的时候有效

        mode: 'local',      // 数据请求方式, local（默认）, api

        field: undefined,   // 字段
        name: undefined,    // 用于api请求用的name，如果不存在，则和field一样

        dict: [],   // 当checkbox，radio的情况下，如果是local模式，需要用到。否则就采用url请求获取
        arrs: [],   // 获取当前表格的取值（去除重复项）

        httpType: undefined,
        http: {},

        batEdit: false, // 是否开启 批量编辑 功能，默认=false，20200426
    };
    let gConfigs = [];

    // v1.3.0，单独设置如下参数
    const defaultSet = {
        pkey: {field: 'id', name: 'id'}, // v1.1.0，单独保存,主表的索引字段field，以及对应http请求的名称name
        mark: {
            strConn: ',', // 字符串 拼接字符（说明是数组），暂不支持修改
            arrConn: ',', // 转换成HTTP请求的数组拼接字符
            rangeConn: '~', // 范围拼接字符，说明该字段是范围，比如1~8
        }, // 数据参数字符
    };
    let gSet = {};
    let gPKey = {};

    const classInit = {
        render: function (myTable, res) {
            gMainTable = myTable;
            gRes = res;
            gData = res.data;

            // 1.加载 预处理模块：crazyQuery, crazyData, crazyHttp
            this.http();
            this.data();    // 保存所有数据
            this.table();   // 保存 table 信息
            this.setting(); // 保存 setting

            // 2.设置 本项目配置
            this.options();
            this.config();
        },
        config: function () {
            // 先获取所有 配置
            if (typeof (gOptions) !== 'object') {
                layer.msg('当前传递参数有误');
                return;
            }

            // 2. 如果没有，则从 cols 中获取 filters 参数
            let cols = gMainTable.cols[0];
            $.each(cols, function (i, col) {
                if (col.hasOwnProperty('filter') && col.hasOwnProperty('field')) {
                    let isNew = true;
                    $.each(gConfigs, function (j, item) {
                        if (item.hasOwnProperty('field') && item.field === col.field) {
                            isNew = false;
                            return false; // break，找到了，说明不是新增的，不添加
                        }
                    });

                    if (!isNew) {
                        return true;  // continue，退出当前循环，进行下一个循环
                    }

                    // 走到这里，说明是新增的field，需要添加
                    let newC = gConfig.deepClone();
                    $.extend(true, newC, col.filter);

                    newC.field = col.field;
                    newC.name = newC.name || col.name || col.field;
                    newC.format = newC.format || (newC.type === 'datetime' ? TIME_FORMAT : undefined);

                    // 保存 http 配置
                    if (!!newC.httpType) {
                        let refHttp = gHttp[newC.httpType];
                        if (!!refHttp && Object.keys(refHttp).length > 0) {
                            let newHttp = {};
                            $.extend(true, newHttp, refHttp);
                            $.extend(true, newHttp, newC.http);
                            newC.http = newHttp;
                        }
                    }
                    // 设置 datetime format
                    if (newC.type === 'datetime') {
                        let fmt = newC.format || {};
                        fmt.req = fmt.req || TIME_FORMAT;
                        fmt.show = fmt.show || TIME_FORMAT;
                        newC.format = fmt;
                    }
                    gConfigs.push(newC);       // 将 config 保存
                }
            });
            log.print('init configs', gConfigs);
        },
        options: function () {
            // 先获取所有 配置
            $.extend(gOptions, defaultOptions);
            $.extend(true, gOptions, gMainTable.filterConfig);
        },
        http: function () {
            gHttp = $Http.options();
            Object.keys(gHttp).length || (gMainTable.httpConfig && $.extend(gHttp, gMainTable.httpConfig) && $Http.render(gHttp));
        },
        data: function () {
            ($Data.getCount && typeof $Data.getCount === 'function') && ($Data.getCount() || $Data.render(gMainTable, gRes));
        },
        table: function () {
            !!$tQuery.tbElem().length || $tQuery.render(gMainTable.elem)
        },
        setting: function () {
            $.extend(true, gSet, defaultSet, gMainTable.setting); // v1.3.0，独立设置信息
            $.extend(gPKey, gSet.pkey);
        }, // 保存setting，v1.3.0
    };

    /**
     * 类构造方法
     * @param myTable
     * @param data
     */
    let currTable = function (myTable, data) {
        console.log('--------- crazyTable Filter ------------------');
        classInit.render(myTable, data);

        gCache.init(); // 初始化默认缓存
        this.main();
    };

    /**
     * 主运行
     */
    currTable.prototype.main = function () {
        let mode = gOptions.mode;
        let clickToShow = function (e, options) {
            e.stopPropagation();
            options.config.arrs = $Data.getColArray(options.config.field); // 获取当前列的值（去重）
            options.logOn = gOptions.logOn || false;
            $Box.render(options); // 弹出层加载
        };

        //默认过滤
        switch (mode) {
            case 'local':
                filter_local();
                break;

            case 'api':
                break;
        }

        let othis = this;

        //遍历过滤项
        layui.each(gConfigs, function (i, config) {
            let configField = config.field;

            // 插入图标
            let th = $tQuery.getTh(configField);
            let value = gCache.get(config.name);

            // 在cell 中添加icon
            let $_icon = $Icon.render(config, MOD_MODE, value, gName.iconId(config.name)); // 图标设置
            $tQuery.getTdCell(th).append($_icon);

            // 增加的btn
            let newBtns = [];
            let btn_cancel = {
                title: '取消过滤', tips: '取消过滤',
                class: 'layui-btn-danger', filter: FORM_CANCEL, icon: 'layui-icon-unlink',
                callback: cancel
            };
            newBtns.push(btn_cancel); // 添加一个按钮：取消过滤
            let btn_batEdit = {
                title: '批量编辑', tips: '批量编辑',
                class: 'layui-btn-warm', filter: FORM_BatEdit, icon: 'layui-icon-snowflake',
                callback: batEdit
            };
            if (!!config.batEdit) {
                newBtns.push(btn_batEdit);
            }

            // 图标点击事件，e=event
            const options = {
                cell: th,
                config: config,
                mode: MOD_MODE,
                value: value,
                newBtns: newBtns,
                submit: submit,
                closed: undefined,
                logOn: gOptions.logOn,
            };
            $_icon.on("click", function (e) {
                clickToShow(e, options, config)
            });
        });
    };

    /**
     * 确认按钮（一般用于 查找）
     * @param opt
     */
    const submit = function (opt) {
        let boxValue = opt.values;
        let config = opt.config;
        let cell = opt.cell;
        let row = opt.row;
        let _$Icon = opt.icon;
        gCache.set(config.name, boxValue.req); // 过滤项写入缓存
        $Icon.has(gName.iconId(config.name)); // 设置 高亮

        switch (config.mode) {
            case 'local':
                filter_local();
                break;

            case 'api':
                filter_api(); //服务端交叉过滤
                break;
        }

        //写入回调函数
        gOptions.done && typeof gOptions.done === 'function' && gOptions.done(gCache.get());
    };

    /**
     * form cancel
     */
    const cancel = function (opt) {
        let boxValue = opt.values;
        let config = opt.config;
        let cell = opt.cell;
        let row = opt.row;
        let configName = config.name;
        let mode = config.mode;

        // 更新 缓存
        gCache.set(configName);
        $Icon.nohas(gName.iconId(config.name)); // 取消 高亮

        switch (mode) {
            case 'local':
                filter_local();
                break;

            case 'api':
                //需要清除where里的对应的值
                let where = {};
                where[configName] = ''; // 清空当前的字段，这样reload的时候，就会取消该字段，但会把其他请求字段带上
                $tQuery.reload({"page": {"curr": 1}, "where": where}); // 开启api搜索的时候，自动回到第一页
                break;
        }

        // done回调
        gOptions.done && typeof gOptions.done === 'function' && gOptions.done(gCache.get());
    };

    /**
     * 批量编辑当前列中选中的单元格
     * @param opt
     */
    const batEdit = function (opt) {
        let boxValue = opt.values;
        let config = opt.config;
        let cell = opt.cell;
        let row = opt.row;

        let mode = gHttp.update.mode; // 获取批量编辑的方式

        let where = {};
        where[config.name] = boxValue[config.name || config.field];

        // 获取当前选中的行记录的一些信息，{ data:[], ids:[], where:{} }
        let checkData = $Data.checkDatas();
        log.print('batEdit get checkStatus Data =>', checkData);
        if (checkData === false) {
            layer.msg('当前未选择任何记录');
            return;
        }
        $.extend(where, checkData.where); // 将当前选中的id对应的where 复制过来

        // before处理
        let trs = $tQuery.getCheckedTrs();
        // log.print('curr checked trs', trs);
        let param = {row: checkData.data, cell: cell, values: boxValue, tr: trs, where: where, config: config};
        log.print('进入 batEdit Before, opt', param);
        config.before && typeof config.before === 'function' && (where = config.before(param));
        if (where === false) { return } // 如果where===false，表示后续的操作要中止。这样可以避免某些不必要的提交

        switch (mode) {
            case 'local': // 更新本地缓存数据
                batEdit_local(config, param);
                break;

            case 'api': // 更新服务器数据
                $.extend(config.http.where, {}, where);
                $Http.httpReq(config.http, true); // 批量编辑，向服务器发起update请求
                $tQuery.reload({"page": {"curr": 1}}); // 开启api更新的时候，重新加载table，自动回到第一页
                break;
        }
    };

    // 批量编辑：本地操作
    const batEdit_local = function (config, opt) {
        log.print('进入 batEdit local, opt', opt);
        let rows = opt.row; // 当前选中行的数据
        let values = opt.values; // 当前过滤窗口设置的值，对象，包含 show, req
        let cell = opt.cell; // 当前操作的单元格（这里是指正在进行批量编辑的列表头）
        let trs = opt.tr; // 当前勾选的行的jQuery对象
        let where = opt.where || {}; // 当前已经设置的http 请求的 where 参数
        let field = config.field;
        let name = config.name;

        // 更新页面table 显示的结果
        $.each(trs, function (i, tr) {
            $tQuery.setCellValue(tr, field, values.show);
        });

        // 更新 $Data 缓存的结果
        $.each(rows, function (i, row) {
            let update = {}
            update[gPKey.name] = row[gPKey.field]; // 保存索引
            update[name] = values.req; // 保存要更新的值
            (name !== field) && (update[field] = values.show); // 保存要更新的值
            $Data.update(update);
        });

        return where;
    };

    /**
     * 本地缓存 和 options配置项 交叉比对
     */
    const filter_local = function () {
        $tQuery.filter($Data.getShowTr(gCache.get())); // 本地缓存 和 options配置项 交叉比对
    };
    /**
     * API 过滤
     */
    const filter_api = function () {
        let where = $Http.getHttpWhere(gCache.get()); // 获取where，将数组转字符串
        $tQuery.reload({"page": {"curr": 1}, "where": where}); // 开启api搜索的时候，自动回到第一页
    };

    const $TIME = new Date().getTime();
    /**
     * cache 缓存：注意，只能存放筛选结果，不能存其他任何数据
     * @type {{init: init, set: set, cache: {}, get: (function(*=): *)}}
     */
    let gCache = {
        /**
         * cache init
         */
        init: function () {
            let elemId = this.getId();
            layui.data(elemId);
            // log.print('localData init');
        },
        /**
         * 过滤项写入缓存
         * @param name
         * @param data
         */
        set: function (name, data) {
            let elemId = this.getId();
            data = data || null;
            if (!data) {
                layui.data(elemId, {key: name, remove: true,}); // 删除本地缓存中的该数据
            } else {
                layui.data(elemId, {key: name, value: data,}); // 保存到本地缓存中
            }
        },
        /**
         * 获取 cache
         * @param name
         * @returns {*|Array}
         */
        get: function (name) {
            let localData = layui.data(this.getId());
            return !name ? localData : (localData[name] || null); // 注意：没有的时候，返回null
        },
        getId: function () {
            return $tQuery.tbId() + '_' + $TIME;
        },
    };

    const log = {
        /**
         * 打印Log
         * @returns {boolean}
         */
        print: function () {
            let logOn = gOptions.logOn || false;
            if (!logOn) return false;
            const tips = 'crazyTable Filter';
            console.log(tips + ' | ', arguments[0], arguments[1] || '',
                arguments[2] || '', arguments[3] || '', arguments[4] || '', arguments[5] || '');
            return true;
        },
    };

    const gName = {
        iconId: function (name) { return 'icon_filter_' + name }
    }

    /**
     * 供外部调用的成员
     *
     * @type {{render: (function(*=): layerTable)}}
     */
    let TBFilter;
    TBFilter = {
        render: function (myTable, data, options) { return new currTable(myTable, data, options); },
        config: function () { return gConfigs; },
        cache: function () { return gCache.get(); },
    };

    //输出接口
    exports(MOD_NAME, TBFilter);
});